package com.dc.datacollect.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dc.datacollect.components.cache.Cache;
import com.dc.datacollect.components.upload.FileResponseServerController;
import com.dc.datacollect.components.upload.FileUploadService;
import com.dc.datacollect.components.upload.UploadFileWrap;
import com.dc.datacollect.components.zip.CompressFileWrap;
import com.dc.datacollect.components.zip.ZIPUtils;
import com.dc.datacollect.constant.BaseConstant;
import com.dc.datacollect.constant.ExportCollectConstant;
import com.dc.datacollect.constant.ExportCollectWorkConstant;
import com.dc.datacollect.constant.type.ThreadPoolType;
import com.dc.datacollect.constant.type.UserSessionActivityKey;
import com.dc.datacollect.dto.UserCollectGroupInfoDTO;
import com.dc.datacollect.entity.Collect;
import com.dc.datacollect.entity.CollectInstance;
import com.dc.datacollect.entity.Collector;
import com.dc.datacollect.helpers.EmailHelper;
import com.dc.datacollect.service.CollectInstanceService;
import com.dc.datacollect.mapper.CollectInstanceMapper;
import com.dc.datacollect.service.CollectService;
import com.dc.datacollect.service.CollectorService;
import com.dc.datacollect.helpers.BeanProvide;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
* @author zhuangjie
* @description 针对表【collect_instance(收集实例表)】的数据库操作Service实现
* @createDate 2023-06-22 08:57:03
*/
@Service
@Slf4j
public class CollectInstanceServiceImpl extends ServiceImpl<CollectInstanceMapper, CollectInstance>
    implements CollectInstanceService{
    @Autowired
    private FileResponseServerController fileResponseServerController;

    @Autowired FileUploadService fileUploadService;
    /**
     * 使用BeanProvide（自己封装的）解决循环依赖
     */
    @Autowired
    private BeanProvide beanProvide;


    @Autowired
    @Qualifier(ThreadPoolType.BASE_THREAD_POOL)
    private ThreadPoolExecutor threadPoolExecutor;


    @Autowired
    private Cache cache;
    @Override
    public CompletableFuture<String> exportCollectHandleFuture(Long collectId)  {
        CompletableFuture<String> future = new CompletableFuture<>();
        threadPoolExecutor.submit(() -> {
            String zipDownloadUrl = null;
            try {
                zipDownloadUrl = repayableExportCollectHandle(collectId);
                // 完成异步操作，没有返回值
                future.complete(zipDownloadUrl);
            } catch (Exception e) {
                future.complete(null);
            }

        });
        return future;
    }

    public void exportCollectForNoticeHandle(Long collectId){
        // 查出收集者
        CollectService collectService = beanProvide.inject(CollectService.class);
        CollectorService collectorService = beanProvide.inject(CollectorService.class);
        Collect collect = collectService.getById(collectId);
        Collector collector = collectorService.getById(collect.getCollectorId());
        String collectorEmail = collector.getEmail();

        log.info("任务结束，导出并邮箱通知！");
        String compressDownloadUrl = null;
        try {
            compressDownloadUrl = repayableExportCollectHandle(collectId);
        } catch (Exception e) {
            try {
                new EmailHelper.Builder("收集任务结束",e.getMessage())
                        .build().send(Arrays.asList(collectorEmail));
            } catch (Exception ex) { }
            return;
        }

        if ( collectorEmail != null ) {
            try {
                String emailBody =  "“"+collect.getTitle()+"”收集已经结束，已经打包好了，下载链接:"+compressDownloadUrl;
                new EmailHelper.Builder("收集任务结束",emailBody)
                        .build().send(Arrays.asList(collectorEmail));
            } catch (Exception e) {
                log.info("重试发送邮件失败！");
            }
        }
    }

    /**
     * 可偿的导出收集程序
     *
     * @param collectId       收集id
     * @return {@link String}
     */
    public String repayableExportCollectHandle(Long collectId) throws Exception {
        for (long l = 0; l < ExportCollectConstant.MAX_RETRY_NUMBER; l++) {
            String compressDownloadUrl = null;
            compressDownloadUrl = exportCollectHandle(collectId);
            if (compressDownloadUrl != null) {
                return compressDownloadUrl;
            }
        }
        return null;
    }

    @Override
    public String exportCollectHandle(Long collectId) throws Exception {
        String exportActiveKey = UserSessionActivityKey.GET_EXPORT_COLLECT_EVENT(collectId);
        try {
            log.info("正在异步导出：{}",collectId);
            CollectService collectService = beanProvide.inject(CollectService.class);
            // 查出收集活动
            Collect collect = collectService.getById(collectId);
            if (collect == null ) {
                throw new Exception("收集活动已经不存在！");
            }
            // 读取所有要压缩的文件url 委托给响应服务返回file 压缩
            LambdaQueryWrapper<CollectInstance> collectInstanceQueryWrapper = new LambdaQueryWrapper<>();
            collectInstanceQueryWrapper.eq(CollectInstance::getCollectId,collectId);
            List<CollectInstance> collectInstanceList = this.list(collectInstanceQueryWrapper);
            if (collectInstanceList == null || collectInstanceList.isEmpty()) {
                throw new Exception("无法压缩，「 "+collect.getTitle()+" 」收集活动没有人参加！");
            }
            Map<String, CollectInstance> ulrAndCIMap = collectInstanceList.stream().collect(Collectors.toMap(ci -> ci.getFile(), ci -> ci));
            List<UserCollectGroupInfoDTO> userCollectGroupInfoDTOList = this.baseMapper.selectUserCollectGroup(collectId);
            Map<Long, UserCollectGroupInfoDTO> userIdAndGroupInfoMap = userCollectGroupInfoDTOList.stream()
                    .collect(Collectors.toMap(userCollectGroupInfo->userCollectGroupInfo.getUserId(),userCollectGroupInfo->userCollectGroupInfo));
            List<UploadFileWrap> uploadFileWrapList = collectInstanceList.stream().map(collectInstance -> new UploadFileWrap(collectInstance.getFile())).collect(Collectors.toList());
            // 根据fileWrap里面的url经处理解析出File，file对象也在里面
            fileResponseServerController.fileWrapProcess(uploadFileWrapList);
            // 准备压缩成功后的文件 (通用位置下/收集者id/收集活动id/形成的压缩包文件)
            UploadFileWrap preUploadFileWrap = fileUploadService.createUploadFileWrap(ExportCollectWorkConstant.WORK_DIR+"/"+collect.getCollectorId()+"/"+collect.getId()+"/", collect.getTitle() + ExportCollectWorkConstant.EXPORT_SUFFIX);

            // 准备压缩的file包装类
            List<CompressFileWrap> compressFileWrapList = uploadFileWrapList.stream().map(uploadFileWrap -> {
                CompressFileWrap compressFileWrap = new CompressFileWrap();
                compressFileWrap.setFile(uploadFileWrap.getFile());
                CollectInstance collectInstance = ulrAndCIMap.get(uploadFileWrap.getUrl());
                if (collectInstance != null) {
                    // 用户id与组的信息
                    UserCollectGroupInfoDTO userCollectGroupInfo = userIdAndGroupInfoMap.get(collectInstance.getUserId());
                    // 获取用户的组名
                    compressFileWrap.setPath(userCollectGroupInfo.getGroupName());
                    compressFileWrap.setFilename(collectInstance.getFileName());
                }
                return compressFileWrap;
            }).collect(Collectors.toList());
            log.info("压缩文件已经准备好，准备压缩{}个文件", compressFileWrapList.size());
            // 文件保存好
            ZIPUtils.zipFiles(compressFileWrapList,preUploadFileWrap.getFile());
            // 传出文件下载链接
            // 查出收集者邮箱
            String hostAndPort = cache.get(BaseConstant.REQUEST_HOST_AND_PORT, String.class);
            String compressDownloadUrl = hostAndPort + preUploadFileWrap.getUrl();
            log.info("”{}“ 已经压缩好了，下载链接：{}",collect.getTitle(),compressDownloadUrl);
            return compressDownloadUrl;
        }finally {
            log.info("开始清理导出操作执行完成的exportActiveKey，收集者可再次发起相同的导出。");
            cache.remove(exportActiveKey);
        }
    }
}




